/*
 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2013
 * Robert Lougher <rob@jamvm.org.uk>.
 *
 * This file is part of JamVM.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/time.h>
#include <limits.h>

#include "jam.h"
#include "thread.h"
#include "hash.h"
#include "alloc.h"
#include "lock.h"
#include "symbol.h"
#include "excep.h"
#include "class.h"
#include "classlib.h"

/* Trace lock operations and inflation/deflation */
#ifdef TRACELOCK
#define TRACE(fmt, ...) jam_printf(fmt, ## __VA_ARGS__)
#else
#define TRACE(fmt, ...)
#endif

#define UN_USED -1

#define HASHTABSZE 1<<5
#define PREPARE(obj) allocMonitor(obj)
#define HASH(obj) (getObjectHashcode(obj) >> LOG_OBJECT_GRAIN)
#define COMPARE(obj, mon, hash1, hash2) hash1 == hash2 && mon->obj == obj
#define FOUND(ptr1, ptr2)                                    \
({                                                           \
     LOCKWORD_COMPARE_AND_SWAP(&ptr2->entering, UN_USED, 0); \
     ptr2;                                                   \
})

/* lockword format in "thin" mode
  63
  31                                         0
  +---------------------------------+-------+-+
  |              thread ID          | count |0|
  +---------------------------------+-------+-+
                                             ^ shape bit

  lockword format in "fat" mode
  63
  31                                         0
  +-----------------------------------------+-+
  |                 Monitor*                |1|
  +-----------------------------------------+-+
                                             ^ shape bit
*/

#define SHAPE_BIT   0x1
#define COUNT_SIZE  8
#define COUNT_SHIFT 1
#define COUNT_MASK  (((1<<COUNT_SIZE)-1)<<COUNT_SHIFT)

#define TID_SHIFT   (COUNT_SIZE+COUNT_SHIFT)
#define TID_SIZE    (32-TID_SHIFT)
#define TID_MASK    (((1<<TID_SIZE)-1)<<TID_SHIFT)

#define SCAVENGE(ptr)                                           \
({                                                              \
    Monitor *mon = (Monitor *)ptr;                              \
    char res = LOCKWORD_READ(&mon->entering) == UN_USED;        \
    if(res) {                                                   \
        TRACE("Scavenging monitor %p (obj %p)", mon, mon->obj); \
        mon->next = mon_free_list;                              \
        mon_free_list = mon;                                    \
    }                                                           \
    res;                                                        \
})

static Monitor *mon_free_list = NULL;
static HashTable mon_cache;

void monitorInit(Monitor *mon) {
    // TODO 这一步是干嘛的啊 将S(mon)的N(sizeof(Monitor) = 96)个字节设置为C（0）。
    memset(mon, 0, sizeof(Monitor));
    // 互斥锁初始化
    pthread_mutex_init(&mon->lock, NULL);
}

void waitSetAppend(Monitor *mon, Thread *thread) {
    if(mon->wait_set == NULL)
        mon->wait_set = thread->wait_prev = thread;

    thread->wait_next = mon->wait_set;
    thread->wait_prev = mon->wait_set->wait_prev;
    thread->wait_prev->wait_next = mon->wait_set->wait_prev = thread;

    thread->wait_id = mon->wait_count++;
}

void waitSetUnlinkThread(Monitor *mon, Thread *thread) {
    if(mon->wait_set == thread)
        if((mon->wait_set = mon->wait_set->wait_next) == thread)
            mon->wait_set = NULL;

    thread->wait_prev->wait_next = thread->wait_next;
    thread->wait_next->wait_prev = thread->wait_prev;
    thread->wait_prev = thread->wait_next = NULL;
}

/* */
Thread *waitSetSignalNext(Monitor *mon) {
    Thread *thread = mon->wait_set;

    if(thread != NULL) {
        waitSetUnlinkThread(mon, thread);
        pthread_cond_signal(&thread->wait_cv);

        thread->notify_id = mon->wait_count;
    }

    return thread;
}
/* 对用Monitor对象加锁  这个方法在获取不到锁时会阻塞当前调用的线程*/
void monitorLock(Monitor *mon, Thread *self) {
    if(mon->owner == self)
        mon->count++;  // 如果锁持有的线程就是当前线程 则只要增加一个计数
    else {
        // 否则调用互斥锁的库方法
        // pthread_mutex_trylock() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。
        if(pthread_mutex_trylock(&mon->lock)) {
            // 说明加锁失败了

            // 关闭挂起
            disableSuspend(self);

            // 设置线程的状态为阻塞状态 且设置线程对应的阻塞的monitor
            self->blocked_mon = mon;
            self->blocked_count++;
            classlibSetThreadState(self, BLOCKED);

            // 当pthread_mutex_lock()返回时，该互斥锁已被锁定。线程调用该函数让互斥锁上锁，如果该互斥锁已被另一个线程锁定和拥有，则调用该线程将阻塞，直到该互斥锁变为可用为止。
            // 所以这里其实就是阻塞当前线程
            pthread_mutex_lock(&mon->lock);

            // 执行到这一步时 线程就不被阻塞了且互斥锁可用
            // 设置状态为运行当中
            // TODO 这里有一个问题，如果多个线程调用pthread_mutex_lock 那么当上一个锁的持有者释放锁时，是所有的线程都会执行到这一步吗？ 应该不能吧 不然就乱套了，毕竟下面并没有重新尝试获取互斥锁的操作
            classlibSetThreadState(self, RUNNING);
            self->blocked_mon = NULL;

            // 启用挂起
            enableSuspend(self);
        }
        // 到这一步 说明获取到了互斥锁 就把monitor的拥有者设置为当前线程
        mon->owner = self;
    }
}

int monitorTryLock(Monitor *mon, Thread *self) {
    if(mon->owner == self)
        mon->count++;
    else {
        if(pthread_mutex_trylock(&mon->lock))
            return FALSE;
        mon->owner = self;
    }

    return TRUE;
}

void monitorUnlock(Monitor *mon, Thread *self) {
    if(mon->owner == self) {
        if(mon->count == 0) {
            mon->owner = NULL;
            pthread_mutex_unlock(&mon->lock);
        } else
            mon->count--;
    }
}

int monitorWait(Monitor *mon, Thread *self, long long ms, int ns,
                int is_wait, int interruptible) {
    char interrupted = interruptible && self->interrupted;

    /* Check we own the monitor */
    if(mon->owner != self)
        return FALSE;

    if(!interrupted) {
        char timed = (ms != 0) || (ns != 0);
        char timeout = FALSE;
        struct timespec ts;
        int old_count;
        int state;

        disableSuspend(self);

        /* Unlock the monitor.  As it could be recursively
           locked remember the recursion count */
        old_count = mon->count;
        mon->owner = NULL;
        mon->count = 0;

        /* Counter used in thin-lock deflation */
        mon->in_wait++;

        self->wait_mon = mon;

        state = timed ? (is_wait ? OBJECT_TIMED_WAIT : SLEEPING)
                      : (is_wait ? OBJECT_WAIT : BLOCKED);

        classlibSetThreadState(self, state);

        if(state == BLOCKED) {
            self->blocked_mon = mon;
            self->blocked_count++;
        } else
            self->waited_count++;

        self->interrupting = FALSE;

        /* Add the thread onto the end of the wait set */
        waitSetAppend(mon, self);

        if(timed)
            getTimeoutRelative(&ts, ms, ns);

        while(self->wait_next != NULL && !self->interrupting && !timeout)
            if(timed) {
                timeout = pthread_cond_timedwait(&self->wait_cv,
                              &mon->lock, &ts) == ETIMEDOUT;

                /* On Linux/i386 systems using LinuxThreads,
                   pthread_cond_timedwait is implemented using
                   sigjmp/longjmp.  This resets the fpu control
                   word back to 64-bit precision.  The macro is
                   empty for sane platforms. */ 

                FPU_HACK;
            } else
                pthread_cond_wait(&self->wait_cv, &mon->lock);

        /* If we've been interrupted or timed-out, we will not have been
           removed from the wait set.  If we have, we must have been
           notified afterwards.  In this case, the notify has been lost,
           and we must signal another thread */

        if(self->interrupting || timeout) {
            /* An interrupt after a timeout remains pending */
            interrupted = interruptible && !timeout;

            if(self->wait_next != NULL)
                waitSetUnlinkThread(mon, self);
            else {
                /* Notify lost.  Signal another thread only if it
                   was on the wait set at the time of the notify */
                if(mon->wait_set != NULL &&
                                mon->wait_set->wait_id < self->notify_id) {
                    Thread *thread = waitSetSignalNext(mon);
                    thread->notify_id = self->notify_id;
                }
            }
        }

        classlibSetThreadState(self, RUNNING);
        self->wait_mon = NULL;

        if(state == BLOCKED)
            self->blocked_mon = NULL;

        /* Restore the monitor owner and recursion count */
        mon->count = old_count;
        mon->owner = self;
        mon->in_wait--;

        enableSuspend(self);
    }

    if(interrupted) {
        self->interrupted = FALSE;
        signalException(java_lang_InterruptedException, NULL);
    }

    return TRUE;
}

int monitorNotify(Monitor *mon, Thread *self) {
    if(mon->owner != self)
        return FALSE;

    /* Signal the first thread in the wait set.  This
       is the thread which has been waiting the longest */
    waitSetSignalNext(mon);

    return TRUE;
}

/* 唤醒所有在当前monitor上的线程*/
int monitorNotifyAll(Monitor *mon, Thread *self) {
    if(mon->owner != self)
        return FALSE;

    /* Signal all threads in the wait set 向等待集中的所有线程发出信号 */
    while(waitSetSignalNext(mon) != NULL);

    return TRUE;
}

Monitor *allocMonitor(Object *obj) {
    Monitor *mon;

    if(mon_free_list != NULL) {
        mon = mon_free_list;
        mon_free_list = mon->next;      
    } else {
        mon = sysMalloc(sizeof(Monitor));
        monitorInit(mon);
    }
    mon->obj = obj;
    /* No need to wrap in LOCKWORD_WRITE as no thread should
     * be modifying it when it's on the free list */
    mon->entering = 0;
    return mon;
}

/* 从obj->的lock中获取mointor的地址，如果不存在则创建一个，并且存到mon_cache这个hash表中*/
Monitor *findMonitor(Object *obj) {
    uintptr_t lockword = LOCKWORD_READ(&obj->lock);

    if(lockword & SHAPE_BIT)  // 已经存在monitor
        return (Monitor*) (lockword & ~SHAPE_BIT);
    else {
        Monitor *mon;
        /* Add if absent, scavenge, locked */
        findHashEntry(mon_cache, obj, mon, TRUE, TRUE, TRUE);
        return mon;
    }
}

// 膨胀
static void inflate(Object *obj, Monitor *mon, Thread *self) {
    TRACE("Thread %p is inflating obj %p...\n", self, obj);
    // TODO 这个像是清除了一个什么标志位 具体调试的时候看看清除乐啥
    clearFlcBit(obj);
    // 给其他线程发信号
    monitorNotifyAll(mon, self);
    // 然后把monitor的地址存放到obj->lock当中
    LOCKWORD_WRITE(&obj->lock, (uintptr_t) mon | SHAPE_BIT);
}

/* 给对象加锁 */
void objectLock(Object *obj) {
    Thread *self = threadSelf();
    // 根据当前线程id 左移 TID_SHIFT = COUNT_SIZE+COUNT_SHIFT = 8 + 1 = 9位 得到一个thin_locked
    uintptr_t thin_locked = self->id<<TID_SHIFT;
    uintptr_t entering, lockword; // unsigned long int 4个字节 32位
    Monitor *mon;

    TRACE("Thread %p lock on obj %p...\n", self, obj);

    // 先尝试把根据当前线程id得到的thin_locked，设置到对象上（期待原本的值是0，也就是没有锁的情况下）
    if(LOCKWORD_COMPARE_AND_SWAP(&obj->lock, 0, thin_locked)) {
        /* This barrier is not needed for the thin-locking implementation;
           it's a requirement of the Java memory model. 
           瘦锁实现不需要这个障碍;这是Java内存模型的要求。*/
        JMM_LOCK_MBARRIER(); // TODO  大概是内存屏障之类的？应该是强制缓存失效吧，把上面设置obj->lock的值让其他寄存器知道，不走缓存，走内存
        return;
    }

    // 如果不为0则表示有锁了

    lockword = LOCKWORD_READ(&obj->lock);  // 获取锁
    /*
        TID_MASK = (((1<<TID_SIZE)-1)<<TID_SHIFT) 
            = (((1<<(32-TID_SHIFT))-1)<<TID_SHIFT) 
            = (((1<<(32-(8+1)))-1)<<(8+1))
            = (((1<<(32-9))-1)<<9)
            =  ((1<<23)-1)<<9
    */

    if((lockword & (TID_MASK|SHAPE_BIT)) == thin_locked) { // 这个应该是判断当前对象锁住的是不是当前线程
        int count = lockword & COUNT_MASK;  // 如果锁住这个对象的就是当前线程 那么就获取加锁的次数

        if(count < (((1<<COUNT_SIZE)-1)<<COUNT_SHIFT))  // 这个应该是判断次数是否达到临界值
            // TODO 这里可以调试一下，应该重复进入就可以
            // 未达到的话 就+1 然后重新写到obj->lock当中
            LOCKWORD_WRITE(&obj->lock, lockword + (1<<COUNT_SHIFT));
        else {
            // 达到了 就会膨胀为重量级锁

            // 查找Monitor
            mon = findMonitor(obj);
            // 加锁 这个方法如果获取不到互斥锁 会进行阻塞
            monitorLock(mon, self);
            // 膨胀 清除了一个标志位，然后给其他线程发信号，再把monitor给存到了obj->lock中
            inflate(obj, mon, self);
            mon->count = 1<<COUNT_SIZE;
        }
        return;
    }

    // 否则的话，就说明当前对象所持有锁的线程不是当前线程

try_again:
    mon = findMonitor(obj);  // 获取一个monitor

try_again2:
    if((entering = LOCKWORD_READ(&mon->entering)) == UN_USED)  // TODO 这个是判断啥
        goto try_again;

    if(!(LOCKWORD_COMPARE_AND_SWAP(&mon->entering, entering, entering+1))) // 这个是使用cas，来设置当前锁的尝试进入的线程 +1
        goto try_again2; // 如果设置失败则重试

    if(mon->obj != obj) {  // 啥情况下，根据obj获取到的mon和mon当中的obj会不一样啊....
        // 但不管如何，如果不一样，则把上面设置过的entering给设置回去
        while(entering = LOCKWORD_READ(&mon->entering),
                        !(LOCKWORD_COMPARE_AND_SWAP(&mon->entering,
                                                    entering, entering-1)));
        goto try_again;
    }

    // 如果获取到了mon也在mon->entering+1了，那么就可以尝试使用monitor加锁了，这个方法会阻塞当前线程
    monitorLock(mon, self);

    // 执行到这一步，应该就是持有锁了
    // TODO 难道说持有锁就会把mon->entering - 1吗 应该是的，具体可以看看monitor相关的文章，在进入owner就是退出entry了
    while(entering = LOCKWORD_READ(&mon->entering),
                    !(LOCKWORD_COMPARE_AND_SWAP(&mon->entering,
                                                entering, entering-1)));
    // 按理说持有锁了，这里肯定obj->lock就有mon的信息了吧？为啥obj->lock会没有呢？
    // TODO 下面的代码看起来确实费劲 需要再好好调试一番
    while((LOCKWORD_READ(&obj->lock) & SHAPE_BIT) == 0) { // 这个判断如果成立 说明obj->lock没有mon的信息
        setFlcBit(obj);

        if(LOCKWORD_COMPARE_AND_SWAP(&obj->lock, 0, thin_locked))  // 没有信息则设置当前锁
            inflate(obj, mon, self);
        else
            monitorWait(mon, self, 0, 0, FALSE, FALSE); // TODO 等待
    }
}

// TODO 对象解锁 
void objectUnlock(Object *obj) {
    Thread *self = threadSelf();
    uintptr_t lockword = LOCKWORD_READ(&obj->lock);
    uintptr_t thin_locked = self->id<<TID_SHIFT;

    TRACE("Thread %p unlock on obj %p...\n", self, obj);

    if(lockword == thin_locked) {
        // 如果标志位里面直接就是薄锁的信息 则替换obj->lock = 0即可

        /* This barrier is not needed for the thin-locking implementation;
           it's a requirement of the Java memory model. 瘦锁实现不需要这个障碍;这是Java内存模型的要求。*/
        JMM_UNLOCK_MBARRIER();
        LOCKWORD_WRITE(&obj->lock, 0);

        /* Required by thin-locking mechanism. 由薄型锁定机构所需。*/
        MBARRIER();

retry:
        if(testFlcBit(obj)) {
            Monitor *mon = findMonitor(obj);

            if(!monitorTryLock(mon, self)) {
                threadYield(self); // 放弃当前线程的执行权
                goto retry;
            }

            if(testFlcBit(obj) && (mon->obj == obj))
                monitorNotify(mon, self);

            monitorUnlock(mon, self);
        }
    } else {
        if((lockword & (TID_MASK|SHAPE_BIT)) == thin_locked)
            LOCKWORD_WRITE(&obj->lock, lockword - (1<<COUNT_SHIFT));
        else
            if((lockword & SHAPE_BIT) != 0) {
                Monitor *mon = (Monitor*) (lockword & ~SHAPE_BIT);

                if((mon->count == 0) && (LOCKWORD_READ(&mon->entering) == 0) &&
                                (mon->in_wait == 0)) {
                    TRACE("Thread %p is deflating obj %p...\n", self, obj);

                    /* This barrier is not needed for the thin-locking
                       implementation; it's a requirement of the Java
                       memory model. 瘦锁实现不需要这个障碍;这是Java内存模型的要求。 */
                    JMM_UNLOCK_MBARRIER();

                    LOCKWORD_WRITE(&obj->lock, 0);
                    LOCKWORD_COMPARE_AND_SWAP(&mon->entering, 0, UN_USED);
                }

                monitorUnlock(mon, self);
            }
    }
}

void objectWait(Object *obj, long long ms, int ns, int interruptible) {
    uintptr_t lockword = LOCKWORD_READ(&obj->lock);
    Thread *self = threadSelf();
    Monitor *mon;

    TRACE("Thread %p Wait on obj %p...\n", self, obj);

    if((lockword & SHAPE_BIT) == 0) {
        int tid = (lockword&TID_MASK)>>TID_SHIFT;
        if(tid == self->id) {
            mon = findMonitor(obj);
            monitorLock(mon, self);
            inflate(obj, mon, self);
            mon->count = (lockword&COUNT_MASK)>>COUNT_SHIFT;
        } else
            goto not_owner;
    } else
        mon = (Monitor*) (lockword & ~SHAPE_BIT);

    if(monitorWait(mon, self, ms, ns, TRUE, interruptible))
        return;

not_owner:
    signalException(java_lang_IllegalMonitorStateException,
                    "thread not owner");
}

void objectNotify(Object *obj) {
    uintptr_t lockword = LOCKWORD_READ(&obj->lock);
    Thread *self = threadSelf();

    TRACE("Thread %p Notify on obj %p...\n", self, obj);

    if((lockword & SHAPE_BIT) == 0) {
        int tid = (lockword&TID_MASK)>>TID_SHIFT;
        if(tid == self->id)
            return;
    } else {
        Monitor *mon = (Monitor*) (lockword & ~SHAPE_BIT);
        if(monitorNotify(mon, self))
            return;
    }

    signalException(java_lang_IllegalMonitorStateException,
                    "thread not owner");
}

void objectNotifyAll(Object *obj) {
    uintptr_t lockword = LOCKWORD_READ(&obj->lock);
    Thread *self = threadSelf();

    TRACE("Thread %p NotifyAll on obj %p...\n", self, obj);

    if((lockword & SHAPE_BIT) == 0) {
        int tid = (lockword&TID_MASK)>>TID_SHIFT;
        if(tid == self->id)
            return;
    } else {
        Monitor *mon = (Monitor*) (lockword & ~SHAPE_BIT);
        if(monitorNotifyAll(mon, self))
            return;
    }

    signalException(java_lang_IllegalMonitorStateException,
                    "thread not owner");
}

int objectLockedByCurrent(Object *obj) {
    uintptr_t lockword = LOCKWORD_READ(&obj->lock);
    Thread *self = threadSelf();

    if((lockword & SHAPE_BIT) == 0) {
        int tid = (lockword&TID_MASK)>>TID_SHIFT;
        if(tid == self->id)
            return TRUE;
    } else {
        Monitor *mon = (Monitor*) (lockword & ~SHAPE_BIT);
        if(mon->owner == self)
            return TRUE;
    }
    return FALSE;
}

Thread *objectLockedBy(Object *obj) {
    uintptr_t lockword = LOCKWORD_READ(&obj->lock);
    Thread *owner;

    if((lockword & SHAPE_BIT) == 0) {
        int tid = (lockword&TID_MASK)>>TID_SHIFT;
        owner = findRunningThreadByTid(tid);
    } else {
        Monitor *mon = (Monitor*) (lockword & ~SHAPE_BIT);
        owner = mon->owner;
    }
    return owner;
}

int initialiseMonitor() {
    /* Init hash table, create lock */
    // 老操作 初始化了一个hash表 大小1<<5=32
    initHashTable(mon_cache, HASHTABSZE, TRUE);

    return TRUE;
}

/* Heap compaction support */

#define ITERATE(ptr) {               \
    Monitor *mon = (Monitor*)ptr;    \
    if(isMarked(mon->obj))           \
         threadReference(&mon->obj); \
}

void threadMonitorCache() {
   hashIterate(mon_cache);
}
